package evmodel;

import java.util.Vector;
import java.util.LinkedList;
import java.util.Hashtable;
import java.util.Enumeration;
import java.io.Serializable;

/**
 * Die Klasse evfleet verwaltet Gruppen von einzelnen Ladevorgängen nach Dissertation
 * von Michael Agsten an der TU Ilmenau 10/2005 - 10/2011 abgeleitet aus dem Mini-E
 * 1.0 Projekt 07/2009 - 07/2010.
 *
 * Ein einzelner Ladevorgang wird immmer beschrieben durch seine technischen und
 * zeitlichen Parameter. Diese sind: plug-in, plug_off, start_charge, end_charge,
 * power und consumption. In dem vorliegenden Modell ist ein einzelner Ldevorgang
 * nach dem vereinfachten CCCV Prinzip (siehe Dissertation M. Agsten) beschrieben.
 * Das bedeutet - jeder Ladevorgang kann durch eine Rechteckfunktion beschrieben
 * werden.
 *
 * Mit dieser Klasse können speziell Lastprofile und/oder Kriterien für die Optimierung der Gruppenlast
 * berechnet werden.
 *
 * @author Michael Agsten, (c) 2010, Ilmenau University of Technology, 2010
 * @author <a href="mailto:agsten.michael@googlemail.com">agsten.michael@googlemail.com</a>
 * @version 1.0
 * @since JDK 1.6
 **/
public class evfleet implements Serializable {

    /**
     * Liste, in der alle individuellen Ladevorgänge enthalten sind.
     */
    public Vector<ev> evs;
    public Hashtable<Integer, LinkedList> time_sorted;

    /**
     * Konstruktur - leer
     */
    public evfleet() {
        this.evs = new Vector();
    }

    /**
     * Konstruktor, der ein Objekt der Klasse charge_events übergeben bekommt. Klont die enthaltenen einzelnen Elemente in seine Liste mit allen Eigenschafte.
     * @param events Objektübergabe der Klasse charge_events
     */
    public evfleet(evfleet events) {
        if (this.evs == null) {
            this.evs = new Vector();
        }
        for (Enumeration<ev> e = events.evs.elements(); e.hasMoreElements();) {
            this.evs.add(e.nextElement().clone());
        }
    }

    /**
     * Methode, die einen einzelnen Ladevorgang der Liste zufügt.
     *
     * @param event
     */
    public void add_charge_event(ev event) {
        if (this.evs == null) {
            this.evs = new Vector();
        }

        this.evs.add(event);
    }

    /**
     * Methode, die mehrere Ladevorgänge der Liste zufügt.
     * @param plugin        : Plug-In Zeit
     * @param plugoff       : Plug-Off Zeit
     * @param ebattery      : Elektrischer Ladebedarf der Batterie in kWh
     * @param eheat         : Elektrischer Heizbedarf des Fahrzeugs in kWh
     * @param mu            : Systemwirkungsgrad AC/DC Wandlers
     * @param cosphi        : cosphi des AC/DC Wandlers
     * @param apower        : Scheinleistung des AC/DC Wandlers
     * @throws Exception
     */
    public void add_charge_event(int plugin[], int plugoff[], double ebattery[], double[] eheat, double[] mu, double[] cosphi, double[] apower) throws Exception {
        if (this.evs == null) {
            this.evs = new Vector();
        }

        for (int ii = 0; ii < plugin.length; ii++) {
            if (plugin[ii] >= 1) {
                ev job = new ev(plugin[ii], plugoff[ii], ebattery[ii], eheat[ii], mu[ii], cosphi[ii], apower[ii]);
                this.add_charge_event(job);
            }
        }
    }

    /**
     * Methode, die das Lastprofil aller Ladevorgänge berechnet. Das Profil wird berechnet im Intervall [0 lenght-1]. Der Vektor des
     * Profils wird zurückgegeben als double[] und hat die Länge length.
     *
     * @param length Länge des zu kalkulierenden Lastprofils
     * @return Lastprofil als double[]
     */
    public double[] get_powershape(int length) {
        double[] shape = new double[length];
        for (int ii = 0; ii < length; ii++) {
            shape[ii] = get_load(ii);
        }
        return shape;
    }

    /**
     * Methode, die das Lastprofil aller Ladevorgänge berechnet. Das Profil wird berechnet im Intervall [start stop]. Der Vektor des
     * Profils wird zurückgegeben als double[] und hat die Länge stop-start +1.
     * @param start Start der Profilberechnung
     * @param stop Ende der Profilberechnung
     * @return Lastprofil als double[]
     */
    public double[] get_powershape(int start, int stop) {
        start--;
        stop--;
        double[] shape = new double[stop - start + 1];
        for (int ii = start; ii < shape.length + start; ii++) {
            shape[ii - start] = get_load(ii);
        }
        return shape;
    }

    /**
     * Methode, die Vorhersagen simuliert --> Ladevorgänge, die erst nach dem gegebenen Zeitschritt ihre Plugin Zeit haben, sind nicht sichtbar
     *
     */

    public double[] get_powershape_forecast(int start, int stop, int time) {
        start--;
        stop--;
        time--;
        double[] shape = new double[stop - start + 1];
        for (int ii = start; ii < shape.length + start; ii++) {
            shape[ii - start] = get_load_forecast(ii, time);
        }
        return shape;
    }


    /**
     * Methode, die das Lastprofil aller Ladevorgänge berechnet. Das Profil wird berechnet im Intervall [0 lenght-1]. Der Vektor des
     * Profils wird zurückgegeben als double[] und hat die Länge length.
     *
     * @param length Länge des zu kalkulierenden Lastprofils
     * @return Lastprofil als double[]
     */
    public double[] get_availableshape(int length) {
        double[] shape = new double[length];
        for (int ii = 0; ii < length; ii++) {
            shape[ii] = get_available(ii);
        }
        return shape;
    }

    /**
     * Methode, die die Verfügbarkeit aller Ladevorgänge berechnet. Das Profil wird berechnet im Intervall [start stop]. Der Vektor des
     * Profils wird zurückgegeben als double[] und hat die Länge stop-start +1.
     * @param start Start der Profilberechnung
     * @param stop Ende der Profilberechnung
     * @return Lastprofil als double[]
     */
    public double[] get_availableshape(int start, int stop) {
        double[] shape = new double[stop - start + 1];
        for (int ii = start; ii < shape.length + start; ii++) {
            shape[ii - start] = get_available(ii);
        }
        return shape;
    }

    /**
     * Methode, die aus anhand des Übergabewertes time die Last aller Elektroautos bestimmt
     * und aggregiert. Damit kann für einen 1/4h Zeitpunkt bestimmt werden, wieviel Leistung die Gruppe aus dem
     * Netz abnimmt.
     * @param time Zeitpunkt in 1/4 Notation
     */
    public double get_load(int time) {
        double load = 0;
        for (Enumeration<ev> e = this.evs.elements(); e.hasMoreElements();) {
            load = load + e.nextElement().charge(time);
        }
        return load;
    }

    public double get_load_forecast(int forecasttime, int time) {
        double load = 0;
        for (Enumeration<ev> e = this.evs.elements(); e.hasMoreElements();) {

            ev element = e.nextElement();

            if ((element.plugin <= time) && (element.managed))
            {
                load = load + element.charge(forecasttime);
            }
        }
        return load;
    }

    /**
     * Methode, die aus anhand des Übergabewertes time die Last aller Elektroautos bestimmt
     * und aggregiert. Damit kann für einen 1/4h Zeitpunkt bestimmt werden, wieviel Leistung die Gruppe aus dem
     * Netz abnimmt.
     * @param time Zeitpunkt in 1/4 Notation
     */
    public double get_available(int time) {
        double avail = 0;
        for (Enumeration<ev> e = this.evs.elements(); e.hasMoreElements();) {
            if (e.nextElement().available(time)) {
                avail = avail + 1;
            }
        }
        return avail;
    }

    /**
     * Methode, die alle von der Klasse verwalteten Ladevorgänge zurückgibt.
     * @return Aufzählung mit allen Ladevorgängen
     */
    public Enumeration<ev> get_events() {
        return this.evs.elements();
    }

    /**
     * @param time
     * @return Enumeration in der alle Ladevorgänge gehalten werden, die zum Zeitpunkt time ihre Plug-In Zeit haben.
     */
    public Enumeration get_events(int time) {

        time = time - 1;
        Vector<ev> job = new Vector();
        for (Enumeration<ev> e = this.evs.elements(); e.hasMoreElements();) {
            ev event = e.nextElement();
            if (event.plugin == time) {
                job.add(event);
            }
        }

        if (job.isEmpty()) {
            return null;
        } else {
            return job.elements();
        }
    }

    /**
     * Methode, die alle Ladevorgänge des Intervalls [start stop] auf das Intervall [0 (7*96)-1] / 1. Woche zurückverschiebt. Damit können
     * aggregierte Ladevorgänge berechnet werden -> zum Beispiel Mittelwerte
     * @param start Startzeitpunkt
     * @param stop Endzeitpunkt
     */
    public void unifie_events(int start, int stop) {
        // scale start and stop time to one week
        // first count all events which are relevant for the timescale [start stop]

        for (Enumeration<ev> e = this.evs.elements(); e.hasMoreElements();) {
            ev event = e.nextElement();
            while (event.plugin > 672) {
                event.shift_to_one_week_before();
            }
        }
    }

    /**
     * Methode, zur Ausgabe der Schaltmatrix - wird genutzt, um Ladevorgänge zeitlich anzuzeigen in Matlab/Simulink Scheduleplot.
     * Intervallänge ist [0 length-1]
     * @param length Intervalllänge
     * @return Schaltmatrix (1/0 Zustände)
     */
    public int[][] get_switch_matrix(int length) {
        int[][] job = new int[this.evs.size()][length];
        int ii = 0;
        for (Enumeration<ev> e = this.evs.elements(); e.hasMoreElements();) {
            ev event = e.nextElement();
            for (int jj = 0; jj < length; jj++) {
                job[ii][jj] = event.is_charging(jj); //due to zero based indexing add 1
            }
            ii++;
        }
        return job;
    }

    /**
     * Methode, zur Ausgabe der Verfügbarkeitsmatrix - wird genutzt, um Ladevorgänge zeitlich anzuzeigen in Matlab/Simulink Scheduleplot.
     * Intervallänge ist [0 length-1]
     * @param length Intervalllänge
     * @return Verfügbarkeits (1/0 Zustände)
     */
    public int[][] get_avail_matrix(int length) {
        int[][] job = new int[this.evs.size()][length];
        int ii = 0;
        for (Enumeration<ev> e = this.evs.elements(); e.hasMoreElements();) {
            ev event = e.nextElement();
            for (int jj = 0; jj < length; jj++) {
                job[ii][jj] = event.is_available(jj); //due to zero based indexing add 1
            }
            ii++;
        }
        return job;
    }

    /**
     * Erzielte Ergebnisse aller Ladevorgänge, die in dieser Klasse verwaltet werden
     * wenn gesteuert wird.
     *
     * @return objective'p
     */
    public double[] get_managed_objective_values() {
        double job[] = new double[this.evs.size()];
        int ii = 0;
        for (Enumeration<ev> e = this.evs.elements(); e.hasMoreElements();) {
            ev event = e.nextElement();
            event.calc_objective_values();
            job[ii] = event.managed_objective_value;
            ii++;
        }
        return job;
    }

    /**
     * Erzielte Ergebnisse aller Ladevorgänge, die in dieser Klasse verwaltet werden
     * wenn nicht gesteuert wird.
     *
     * @return objective'p
     */
    public double[] get_unmanaged_objective_values() {
        double job[] = new double[this.evs.size()];
        int ii = 0;
        for (Enumeration<ev> e = this.evs.elements(); e.hasMoreElements();) {
            ev event = e.nextElement();
            event.calc_objective_values();
            job[ii] = event.unmanged_objective_value;
            ii++;
        }
        return job;
    }

    /**
     * 
     *
     * @return Summen der Ladevorgänge, nicht skaliert auf die Abtastarte T
     */
    public double[] get_shape_sums() {
        double job[] = new double[this.evs.size()];
        int ii = 0;
        for (Enumeration<ev> e = this.evs.elements(); e.hasMoreElements();) {
            ev event = e.nextElement();
            job[ii] = event.sum_shape();
            ii++;
        }
        return job;
    }
}
